package cn.qyd.aqs;

import java.util.concurrent.locks.StampedLock;

/**
 * @author 邱运铎
 * @date 2024-03-18 23:53
 */
public class StampedLockDemo {
    private double x, y;
    private final StampedLock sl = new StampedLock();

    public void move(double deltaX, double deltaY) {
        long stamp = sl.writeLock();
        try {
            x += deltaX;
            y += deltaY;
            System.out.println(Thread.currentThread().getName() + "move X = " + x + "  y = " + y);
        } finally {
            sl.unlockWrite(stamp);
        }
    }

    public double distanceFromOrigin() {
        long stamp = sl.tryOptimisticRead();
        double currentX = x, currentY = y;
        if (!sl.validate(stamp)) {
            stamp = sl.readLock();
            try {
                currentX = x;
                currentY = y;
            } finally {
                sl.unlockRead(stamp);
            }
        }
        System.out.println(Thread.currentThread().getName() + "distantFromOrigin X = " + x + "  y = " + y);
        return Math.sqrt(currentX*currentX +currentY*currentY);
    }

    public void moveIfAtOrigin(double newX, double newY) {
        long stamp = sl.readLock();
        try {
            while (x == 0.0 && y == 0) {
                long ws = sl.tryConvertToWriteLock(stamp);
                if (ws != 0L) {
                    stamp = ws;
                    x = newX;
                    y = newY;
                    System.out.println(Thread.currentThread().getName() + "init X = " + x + "  y = " + y);
                } else {
                    sl.unlockRead(stamp);
                    stamp = sl.writeLock();
                }
            }
        } finally {
            sl.unlock(stamp);
            System.out.println(Thread.currentThread().getName() + "moveIfAtOrigin X = " + x + "  y = " + y);
        }
    }

    public static void main(String[] args) {
        StampedLockDemo demo = new StampedLockDemo();
        Runnable initTask1 = () -> {
            demo.moveIfAtOrigin(1,1);
        };
        Runnable initTask2 = () -> {
            demo.moveIfAtOrigin(2,2);
        };
        Runnable initTask3 = () -> {
            demo.moveIfAtOrigin(3,3);
        };
        Runnable[] initTasks = new Runnable[]{initTask1,initTask2,initTask3};

        for (int i = 0; i < initTasks.length; i++) {
            new Thread(initTasks[i]).start();
        }

        Runnable moveTask =() -> {
            for (int i = 0; i < 10; i++) {
                demo.move(i,i+1);
            }
        };

        Runnable distantTask = () -> {
            for (int i = 0; i < 10; i++) {
                demo.distanceFromOrigin();
            }
        };

        for (int i = 0; i < 5; i++) {
            new Thread(moveTask).start();
            new Thread(distantTask).start();
        }

        while (Thread.activeCount() > 2) {
            Thread.yield();
        }

        System.out.println("demo X = " + demo.x + "  y = " + demo.y);
    }
}
